import { create } from 'zustand';
import { combine, persist, createJSONStorage } from 'zustand/middleware';
import { Updater } from '../typing';
import { deepClone } from './clone';
import { indexedDBStorage } from './indexedDB-storage';

type SecondParam<T> = T extends (
    _f: infer _F,
    _s: infer S,
    ...args: infer _U
) => any
    ? S
    : never;

type MakeUpdater<T> = {
    lastUpdateTime: number;

    markUpdate: () => void;
    update: Updater<T>;
};

type SetStoreState<T> = (
    partial: T | Partial<T> | ((state: T) => T | Partial<T>),
    replace?: boolean | undefined
) => void;

export function createPersistStore<T extends object, M>(
    state: T,
    methods: (
        set: SetStoreState<T & MakeUpdater<T>>,
        get: () => T & MakeUpdater<T>
    ) => M,
    persistOptions: SecondParam<typeof persist<T & M & MakeUpdater<T>>>
) {
    persistOptions.storage = createJSONStorage(() => indexedDBStorage);
    return create(
        persist(
            combine(
                {
                    ...state,
                    lastUpdateTime: 0,
                },
                (set, get) => {
                    return {
                        ...methods(set, get as any),

                        markUpdate() {
                            set({ lastUpdateTime: Date.now() } as Partial<
                                T & M & MakeUpdater<T>
                            >);
                        },
                        update(updater) {
                            const state = deepClone(get());
                            updater(state);
                            set({
                                ...state,
                                lastUpdateTime: Date.now(),
                            });
                        },
                    } as M & MakeUpdater<T>;
                }
            ),
            persistOptions as any
        )
    );
}
